﻿using System;
using System.Collections;
using System.Data.Common;
using System.Reflection;
using Vbyte.SharpOrm.Config;
using Vbyte.DataSource.Cache;

namespace Vbyte.SharpOrm
{
    public static partial class OrmHelper
    {
        #region 实体绑定

        /// <summary>
        /// 使用已打开的共享连接，绑定单条实例映射数据。
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        /// <param name="SharedOpenConnection">已打开的共享数据库连接，只使用不关闭及销毁。</param>
        public static void DataBind(this TableEntry SqlTableEntry, string[] selectFields, DbConnection SharedOpenConnection)
        {
            DbCommand cmd = GeneratorDbCommand(SqlTableEntry, SQLRowCommandType.Select, null, selectFields);
            if (SharedOpenConnection != null)
            {
                cmd.Connection = SharedOpenConnection;
            }
            else
            {
                cmd.Connection = new AdoNetAdapter(SqlTableEntry).GetDbConnection();
                cmd.Connection.Open();
            }

            DbDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow);
            //OrmHelper.Debug(cmd.CommandText);

            bool blnNotExist = false;
            if (reader.HasRows && reader.Read())
            {
                #region DataReader绑定数据
                Type t = SqlTableEntry.GetType();
                string propName = "";
                PropertyInfo[] Properties = GenericCache<PropertyInfo[]>.Get(t.FullName, () => t.GetProperties());
                foreach (PropertyInfo pi in Properties)
                {
                    propName = pi.Name;

                    //所有命令操作忽略
                    if (HasAttribute(pi, typeof(CommandIgnoreAttribute), true))
                    {
                        continue;
                    }

                    //标记有映射转换
                    TableItemAttribute[] itemAttr = GetMemberInfoCustomAttributes<TableItemAttribute>(pi, true);
                    if (itemAttr.Length > 0)
                    {
                        propName = itemAttr[0].BindField;
                    }
                    try
                    {
                        //有可能没有对应字段名称
                        if (reader.GetOrdinal(propName) > -1)
                        {
                            pi.SetValue(SqlTableEntry, reader[propName], null);
                        }
                    }
                    catch (Exception) { }
                }
                #endregion
            }
            else
            {
                blnNotExist = true;
            }
            reader.Close();
            reader.Dispose();

            //释放自动打开的数据连接
            if (SharedOpenConnection == null)
            {
                cmd.Connection.Close();
                cmd.Connection.Dispose();
            }
            cmd.Dispose();

            if (blnNotExist) throw new NotExistException("没有找到对应实例记录！");
        }

        /// <summary>
        /// 单条的数据绑定
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        /// <param name="selectFields">列表获取的实体属性名集合，忽略属性将保持其默认值。</param>
        public static void DataBind(this TableEntry SqlTableEntry, string[] selectFields)
        {
            DataBind(SqlTableEntry, selectFields, null);
        }

        /// <summary>
        /// 单条的数据绑定
        /// </summary>
        /// <param name="SqlTableEntry">表映射实例</param>
        public static void DataBind(this TableEntry SqlTableEntry)
        {
            DataBind(SqlTableEntry, new string[0], null);
        }

        /// <summary>
        /// 在找不到对应的实体属性名称时调用委托
        /// </summary>
        public delegate void OnEntryMissingProperty(string propName, object propValue);

        /// <summary>
        /// 从扩充的数据键值对中绑定实体
        /// </summary>
        /// <typeparam name="T">实例类型</typeparam>
        /// <param name="instance">该实体</param>
        /// <param name="instanceData">需要绑定的字段集合</param>
        /// <param name="missCall">在找不到对应的实体属性名称时的回调引用</param>
        /// <returns></returns>
        public static T DataBind<T>(this T instance, DictionaryEntry[] instanceData, OnEntryMissingProperty missCall)
            where T : new()
        {
            if (instance == null) return default(T);

            Type t = instance.GetType();
            foreach (DictionaryEntry entry in instanceData)
            {
                if (entry.Key != null && !string.IsNullOrEmpty(entry.Key.ToString()))
                {
                    PropertyInfo pi = t.GetProperty(entry.Key.ToString());
                    if (pi != null)
                    {
                        if (entry.Value != null && pi.PropertyType != entry.Value.GetType())
                        {
                            pi.SetValue(instance, Convert.ChangeType(entry.Value, pi.PropertyType), null);
                        }
                        else
                        {
                            pi.SetValue(instance, entry.Value, null);
                        }
                    }
                    else
                    {
                        if (missCall != null) missCall(entry.Key.ToString(), entry.Value);
                    }
                }
            }
            return instance;
        }
        #endregion
	}
}
